﻿using System;
using System.Diagnostics;
using System.Threading;
using System.Windows;
using ThePresenter.Net.NetCom;
using ThePresenter.Net.PresenterCom;
using ThePresenter.Properties;
using ThePresenter.Windows;

namespace ThePresenter
{
	class ProgramInstance
	{
		#region Exit codes
		public const int NOT_RUNNING = -2;
		public const int RUNNING = -1;
		public const int EXIT_NORMAL = 0;
		public const int EXIT_ABNORMAL = 1;
		public const int EXIT_USER = 2;
		public const int EXIT_NETWORK_ERROR = 3;
		#endregion

		#region Fields
		/// <summary>
		/// Presentation host.
		/// </summary>
		PresentationNetworkClient _Host;

		/// <summary>
		/// StartupMode used to launch instance.
		/// </summary>
		StartupMode _StartupMode;

		/// <summary>
		/// Name of program instance or null if not named.
		/// </summary>
		private String _InstanceName;

		/// <summary>
		/// Instance window.
		/// </summary>
		private OverlayWindow _Window;

		/// <summary>
		/// Holds current instance state.
		/// </summary>
		private int _State;
		#endregion

		#region Properies
		/// <summary>
		/// Gets the state of instance.
		/// </summary>
		/// <value>The state of instance.</value>
		internal int State
		{
			get { return _State; }
		}

		/// <summary>
		/// Gets or sets the host.
		/// </summary>
		/// <value>The host.</value>
		internal PresentationNetworkClient Host
		{
			get
			{
				return _Host;
			}
			set
			{
				_Host = value;
			}
		}

		/// <summary>
		/// Gets a value indicating whether this instance is network.
		/// </summary>
		/// <value><c>true</c> if this instance is network; otherwise, <c>false</c>.</value>
		internal bool NetworkEnabled
		{
			get
			{
				return (_Host != null);
			}
		}

		/// <summary>
		/// Gets the startup mode.
		/// </summary>
		/// <value>The startup mode.</value>
		internal StartupMode StartupMode
		{
			get
			{
				return _StartupMode;
			}
		}
		#endregion

		#region Constructors
		/// <summary>
		/// Initializes a new instance of the <see cref="ProgramInstance"/> class.
		/// </summary>
		internal ProgramInstance()
			: this(String.Empty)
		{
		}

		/// <summary>
		/// Initializes a new named instance of the <see cref="ProgramInstance"/> class.
		/// </summary>
		internal ProgramInstance(String InstanceName)
		{
			this._InstanceName = InstanceName;
			this._State = NOT_RUNNING;
		}
		#endregion

		#region Event handlers
		/// <summary>
		/// Handles the Closed event of the Window control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		void Window_Closed(object sender, EventArgs e)
		{
			if (_Host != null)
			{
				_Host.Dispose();
			}
		}
		#endregion

		#region Methods
		/// <summary>
		/// Starts in the specified mode.
		/// </summary>
		/// <param name="mode">The mode.</param>
		/// <exception cref="Systen.ArgumentException"/>
		internal void Start(StartupMode Mode)
		{
			Debug.WriteLine("StartupMode: " + Mode);

			_StartupMode = Mode;

			if (Mode == StartupMode.Prompt)
			{
				StartupWindow prompt_dlg = new StartupWindow();

				if (prompt_dlg.ShowDialog() == true & prompt_dlg.StartupMode != StartupMode.Prompt)
				{
					Mode = prompt_dlg.StartupMode;
				}
				else
				{
					Shutdown(EXIT_USER);
					return;
				}
			}

			_Window = new OverlayWindow();
			_Window.Mode = Mode;
			_Window.Closed += new EventHandler(Window_Closed);


			try
			{
				switch (Mode)
				{
					#region Standalone
					case StartupMode.Standalone:
						// no network
						break;
					#endregion

					#region Presenter
					case StartupMode.Presenter:
						CheckUsername();

						Host = new PresentationHost(Settings.Default.DefaultApplicationPort);
						Host.Bind(_Window.Presentation);

						break;
					#endregion

					#region Watcher
					case StartupMode.Watcher:
						#region Server Connection Setup
						ManualResetEvent ConnectionFinished = new ManualResetEvent(false);

						bool connectionSuccesful = false;
						while (!connectionSuccesful)
						{
							try
							{
								ServerSelectionDialog SSD = new ServerSelectionDialog();
								ServerInfo SI = SSD.SelectServerDialog(); // throws NoSuitableInterfacesException

								if (SI != null)
								{
									CheckUsername();

									PresentationViewer host = new PresentationViewer(Settings.Default.Username);
									host.Bind(_Window.Presentation);

									host.Connect(SI, ((Info, Succ) =>
									{
										// MessageBox.Show("Connected: " + Info.ToString() + Succ);
										if (Succ)
										{
											Host = host;
											SSD.Dispose();
											connectionSuccesful = true;

											ConnectionFinished.Set();
										}
									}));
								}
								else
								{
									//if (MessageBox.Show("You must select server to continue. Want to search again?", "Network error", MessageBoxButton.YesNo, MessageBoxImage.Error) != MessageBoxResult.Yes)
									//{
									//    SSD.Dispose();
									//    break;
									//}
									ConnectionFinished.Set();

									break;
								}
							}
							catch (NoSuitableInterfacesException)
							{
								ConnectionFinished.Set();

								MessageBox.Show("Server could not be started because there are no valid network interfaces", "Network error", MessageBoxButton.OK, MessageBoxImage.Error);
								Shutdown(EXIT_NETWORK_ERROR);

								break;
							}

							ConnectionFinished.WaitOne();
						}

						if (!connectionSuccesful)
						{
							Shutdown(EXIT_USER); // User dont want to search again

							return;
						}
						#endregion
						break;
					#endregion

					#region RemoteProjector
					//case StartupMode.RemoteProjector:
					//	// TODO RemoteProjector mode
					//	break;
					#endregion

					#region others
					default:
						throw new ArgumentException("Only Standalone, Presenter and Watcher modes are supported.", "mode");
					#endregion
				}
			}
			catch (System.Net.Sockets.SocketException ex)
			{
				//if (ex.ErrorCode == 100048) // Only one usage of each socket address (protocol/network address/port) is normally permitted
				MessageBox.Show(string.Format("Server could not be started, because socket exception occured:\r\n\"{0}\".", ex.Message), "Network error", MessageBoxButton.OK, MessageBoxImage.Error);
				Shutdown(EXIT_NETWORK_ERROR);

				return;
			}
			catch (NoSuitableInterfacesException)
			{
				MessageBox.Show("Server could not be started because there are no valid network interfaces", "Network error", MessageBoxButton.OK, MessageBoxImage.Error);
				Shutdown(EXIT_NETWORK_ERROR);

				return;
			}

			_Window.Show();
		}

		private void CheckUsername()
		{
			InputNameBox INB = new InputNameBox();
			INB.Username = Settings.Default.Username;

			if (INB.ShowDialog().Value == true)
			{
				Settings.Default.Username = INB.Username;
			}
			else
			{
				Settings.Default.Username = System.Environment.MachineName;
				MessageBox.Show("Username cannot be empty.\nUsing machine name (" + System.Environment.MachineName + ") as username.", "", MessageBoxButton.OK, MessageBoxImage.Information);
			}
		}

		/// <summary>
		/// Shutdowns with the specified exit code.
		/// </summary>
		/// <param name="ExitCode">The exit code.</param>
		private void Shutdown(int ExitCode)
		{
			if (_Window != null)
			{
				_Window.Close();
				_Window = null;
			}

			_State = ExitCode;

			OnAfterShutdown();
		}

		/// <summary>
		/// Raises the <see cref="AfterShutdown"/> event.
		/// </summary>
		protected void OnAfterShutdown()
		{
			if (AfterShutdown != null)
				AfterShutdown(this, EventArgs.Empty);
		}
		#endregion

		#region Events
		// TODO App should handle AfterShutdown event.
		public event EventHandler AfterShutdown;
		#endregion
	}
}
